// SenfeDlg.cpp : implementation file
//
#include <ctype.h>
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include "stdafx.h"
#include "Senfe.h"
#include "SenfeDlg.h"
#include "task.h"
#include "feasibility.h"
#include "types.h"
#include "senfe_resource.h"
#include "graph.h"

#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif

struct task_resource_use task_res[20][50]; //each task's resource table
struct Senfe_task_attr task_attr[20];		// task's attr
struct Senfe_task_abs t_abs[20];			//the real task

				
struct Senfe_res_table task_res_table[50];  //all format of used resource
u8 task_res_table_number = 0;				//the number of task_res_table

u8 task_number = 0;							//the number of task attr
int result_flag;							//1:success 0:fail



//draw buffer
int countw=0;
int counth=0;
int countb=0;
int timeend;
float Wload_t[50];
float Wload_W[50];
float Hload_t[50];
float Hload_H[50];
float Bload_t[30];
float Bload_B[30];

/////////////////////////////////////////////////////////////////////////////
// CSenfeDlg dialog

CSenfeDlg::CSenfeDlg(CWnd* pParent /*=NULL*/)
	: CDialog(CSenfeDlg::IDD, pParent)
{
	//{{AFX_DATA_INIT(CSenfeDlg)
	m_deadline = 0;
	m_period = 0;
	m_resource = _T("");
	m_runtime = 0;
	m_delay = 0;
	//}}AFX_DATA_INIT
	m_hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME);
}

void CSenfeDlg::DoDataExchange(CDataExchange* pDX)
{
	CDialog::DoDataExchange(pDX);
	//{{AFX_DATA_MAP(CSenfeDlg)
	DDX_Control(pDX, IDC_BUTTON1_feasibility, m_feasibility);
	DDX_Control(pDX, IDC_LIST_task, m_tasklist);
	DDX_Text(pDX, IDC_editDeadline, m_deadline);
	DDX_Text(pDX, IDC_editPeriod, m_period);
	DDX_Text(pDX, IDC_editResource, m_resource);
	DDX_Text(pDX, IDC_editRuntime, m_runtime);
	DDX_Text(pDX, IDC_editDelay, m_delay);
	//}}AFX_DATA_MAP
}

BEGIN_MESSAGE_MAP(CSenfeDlg, CDialog)
	//{{AFX_MSG_MAP(CSenfeDlg)
	ON_WM_PAINT()
	ON_WM_QUERYDRAGICON()
	ON_BN_CLICKED(ID_button_creattask, Onbuttoncreattask)
	ON_BN_CLICKED(IDC_BUTTON1_feasibility, OnBUTTON1feasibility)
	ON_BN_CLICKED(IDC_CLEAR_BUTTON, OnClearButton)
	//}}AFX_MSG_MAP
END_MESSAGE_MAP()

/////////////////////////////////////////////////////////////////////////////
// CSenfeDlg message handlers

BOOL CSenfeDlg::OnInitDialog()
{
	CDialog::OnInitDialog();

	SetIcon(m_hIcon, TRUE);			// Set big icon
	SetIcon(m_hIcon, FALSE);		// Set small icon
	
	// TODO: Add extra initialization here

	m_feasibility.EnableWindow(false);  //invalidate the feasibility button

	/*initial the list control*/
	//DWORD styles = LVS_EX_FULLROWSELECT|LVS_EX_GRIDLINES|LVS_EX_CHECKBOXES;
	DWORD styles = LVS_EX_FULLROWSELECT|LVS_EX_GRIDLINES;
	ListView_SetExtendedListViewStyleEx(m_tasklist.m_hWnd, styles, styles );
	m_tasklist.InsertColumn(0,"Index");
	m_tasklist.InsertColumn(1,"Deadline");
	m_tasklist.InsertColumn(2,"Period");
	m_tasklist.InsertColumn(3,"Runtime");
	m_tasklist.InsertColumn(4,"Delay");
	m_tasklist.InsertColumn(5,"Resource");

	CRect rect4;
	m_tasklist.GetClientRect(rect4);                    
	m_tasklist.SetColumnWidth(0,rect4.Width()/10);       
	m_tasklist.SetColumnWidth(1,rect4.Width()*3/20);
	m_tasklist.SetColumnWidth(2,rect4.Width()*3/20);
	m_tasklist.SetColumnWidth(3,rect4.Width()*3/20);
	m_tasklist.SetColumnWidth(4,rect4.Width()*3/20);
	m_tasklist.SetColumnWidth(5,rect4.Width()*9/20);	
	return TRUE;  // return TRUE  unless you set the focus to a control
}


void CSenfeDlg::OnPaint() 
{
	if (IsIconic())
	{
		CPaintDC dc(this); // device context for painting

		SendMessage(WM_ICONERASEBKGND, (WPARAM) dc.GetSafeHdc(), 0);

		// Center icon in client rectangle
		int cxIcon = GetSystemMetrics(SM_CXICON);
		int cyIcon = GetSystemMetrics(SM_CYICON);
		CRect rect;
		GetClientRect(&rect);
		int x = (rect.Width() - cxIcon + 1) / 2;
		int y = (rect.Height() - cyIcon + 1) / 2;

		// Draw the icon
		dc.DrawIcon(x, y, m_hIcon);
	}
	else
	{
		CDialog::OnPaint();
	}
}

HCURSOR CSenfeDlg::OnQueryDragIcon()
{
	return (HCURSOR) m_hIcon;
}

void CSenfeDlg::Onbuttoncreattask() 
{
	// TODO: Add your control notification handler code here
	float res_time;
	m_feasibility.EnableWindow(true);//enable feasibility button
	
	CString itemtext0,itemtext1,itemtext2,itemtext3,itemtext4;
	UpdateData(true);
	/*fill the task_attr with input*/
	task_attr[task_number].deadline = m_deadline;
	task_attr[task_number].period = m_period;
	task_attr[task_number].cputime = m_runtime;
	task_attr[task_number].delay = m_delay;
	task_attr[task_number].resource_use = task_res[task_number];
	
	CString resource_input = m_resource;
	res_time = Resource_feasibility(resource_input,task_number);
	
	/*check input*/
	if(m_runtime <= m_deadline && m_deadline <= m_period && m_runtime >= res_time){
		
		/*add the input to list control*/
		itemtext0.Format("%d",task_number);
		itemtext1.Format("%d",m_deadline);
		itemtext2.Format("%d",m_period);
		itemtext3.Format("%d",m_runtime);
		itemtext4.Format("%d",m_delay);
		m_tasklist.InsertItem(task_number,itemtext0); 
		m_tasklist.SetItemText(task_number,1,itemtext1); 
		m_tasklist.SetItemText(task_number,2,itemtext2);    
		m_tasklist.SetItemText(task_number,3,itemtext3);
		m_tasklist.SetItemText(task_number,4,itemtext4);
		m_tasklist.SetItemText(task_number,5,m_resource);
		m_deadline = 0;
		m_period = 0;
		m_resource = _T("");
		m_runtime = 0;
		m_delay = 0;
		UpdateData(false);
		task_number ++;
	} else{
		AfxMessageBox("Please check your input parameter!");
	}

}
/*analysis the input resource string*/
float CSenfeDlg::Resource_feasibility(CString res_input, UINT task_index)
{
	int all_number,i,res_number,same_flag;
	float cost_value,cost_all;
	char char_tmp;
	CString number_buffer,char_buffer;
	
	all_number = res_input.GetLength(); //the number of input letters

	number_buffer = _T("");
	char_buffer = _T("");
	i = 0;
	res_number = 0;
	cost_all=0;
	while(i < all_number)
	{

		char_tmp = res_input.GetAt(i);
		if(isdigit(char_tmp) || char_tmp == '.')   // join all the number character together
		{	
				number_buffer += char_tmp;
		}
		else if(char_tmp == '{')         // meet '{' ,turn the number_buffer to int
		{
				cost_value = atof(number_buffer);
				if(cost_value == 0)
					cost_value = m_runtime;
				
				number_buffer = _T(""); //clear number_buffer;
		}
		else if(IsCharAlpha(char_tmp))   // meet a letter ,join all 
		{
				char_buffer += char_tmp;

		}
		else if(char_tmp == '}')        // meet '}' 
		{
			if(char_buffer.GetLength() == 1)
			{	
				char j = char_buffer.GetAt(0);
				int char_index;
				
				if(j <= 'z' && j >= 'a' ) 
				{
					task_res[task_index][res_number].type = SH;
					char_index = j - 0x61;
				} else {
					task_res[task_index][res_number].type = EX;
					char_index = j - 0x41;
				}
				task_res[task_index][res_number].res_ptr = &all_res[char_index];
				task_res[task_index][res_number].cost = cost_value;
				cost_all +=cost_value;
				res_number ++;
			}else{
				cost_all -= cost_value;
			}
			char_buffer.MakeLower();
			int k;
			for(k=0; k<= task_res_table_number ;k++)
			{
				if (char_buffer.Compare(task_res_table[k].name) == 0) {
					
					same_flag = 1;
					break;
				} else {
					same_flag = 0;
				}
			}
			if(same_flag == 0)
			{
				task_res_table[task_res_table_number].name = char_buffer;

				task_res_table_number ++;
			}

			char_buffer = _T("");//clear char_buffer
		}
		else
			;

		i++; //analysis next character

	}
	return cost_all;
}

void CSenfeDlg::OnBUTTON1feasibility() 
{
	// TODO: Add your control notification handler code here
	int i;
	int j;
	int k;
	UINT temp_delay;

	for(i=0; i<task_number; i++)
	{
		if (i == 0){ // insert the first one
			t_abs[i].attr = &task_attr[i];
			t_abs[i].absolute = task_attr[i].delay;
			t_abs[i].delta = 0;
		}else{
			temp_delay =  task_attr[i].delay;
			for(j = 0; j < i; j++ ){
				if( temp_delay > t_abs[j].absolute)
					temp_delay -= t_abs[j].absolute;
				else
					break;
			}

			if ( j == i){ // insert to the array tail
				t_abs[i].attr = &task_attr[i];
				t_abs[i].absolute = temp_delay;
				t_abs[i].delta = 0;
			}else{ // insert to the head or middle of the array
				for ( k = i; k > j; k--){
					t_abs[k] = t_abs[k -1];
				}
				t_abs[k].attr = &task_attr[i];
				t_abs[k].absolute = temp_delay;
				t_abs[k].delta = 0;
				t_abs[k+1].absolute -= temp_delay;
			}

		}	
	} 

	for(i=0; i<task_number; i++){
		t_abs[i].next = ( i < (task_number-1)) ? &(t_abs[i+1]) : NULL;
	}

	result_flag = feasibility_analysis(t_abs);

	res_table_calc_delta(task_res_table);
	
	m_feasibility.EnableWindow(false);

	if(result_flag){
		FILE *fp;
		fp = fopen("resourcetable.txt","w");
		if(fopen == NULL){
			AfxMessageBox("file open error");
			exit(0);
		}
		
		for(i=0;i<task_res_table_number;i++)
		{
			fprintf(fp, "%s :  %d\n", task_res_table[i].name, task_res_table[i].deadline);
		}
		fclose(fp);
	}
		

	CGRAPH * tmp_CDlgptr;
	tmp_CDlgptr = new CGRAPH;
	tmp_CDlgptr->Create(IDD_GRAPH_DIALOG,this);
	tmp_CDlgptr->ShowWindow(SW_NORMAL);


}

static struct Senfe_task_abs *
task_abs_queue_first(struct Senfe_task_abs **taskp)
{
	struct Senfe_task_abs *task;
	
	//assert( taskp != NULL );
	
	task = *taskp;
	if (task != NULL)
	{
		*taskp = task->next;//change the header
		if ( task->next != NULL )
			task->next->absolute += task->absolute;//the task->absolute always be 0??
		task->next = NULL;
	}
	
	return task;
}


/* req: locking on taskp */
static void
task_abs_queue_insert(struct Senfe_task_abs **taskp, struct Senfe_task_abs *task)
{
	//assert( taskp != NULL );
	//assert( task != NULL );
	//assert( task->next == NULL );
	
	while (*taskp != NULL)
	{
		if (task->absolute < (*taskp)->absolute)
		{
			task->next = *taskp;
			task->next->absolute -= task->absolute;//adjust the origen header's time
			*taskp = task;
			return;
		}
		
		task->absolute -= (*taskp)->absolute;
		taskp = &((*taskp)->next);
	}
	
	*taskp = task;//only used when queue empty
	return;
}


void
scheduler_calc_delta( struct Senfe_task_abs *new_task_set )
{
	struct Senfe_task_abs *task;
	
	// Initialize all resources to max. deadline
	for ( task = new_task_set; task != NULL; task = task->next )
	{
		task_resource_use_ptr rp;
		
		// exclusive use of resources
		if ((rp = task->attr->resource_use) != NULL)
			for ( ; (rp->res_ptr) != NULL ; rp ++ )
				rp->res_ptr->deadline = 0xffffffff;
			
	}
	
	// Calculate the resource inherited deadlines (exclusive use of resource)
	for ( task = new_task_set; task != NULL; task = task->next )
	{
		task_resource_use_ptr rp;
		
		if ((rp = task->attr->resource_use) != NULL)
			for ( ; (rp->res_ptr) != NULL ; rp ++ ){
				if((rp)->type == 1){
					if (rp->res_ptr->deadline > task->attr->deadline)
						rp->res_ptr->deadline = task->attr->deadline;
				}
			}
	}
	
}

void
res_table_calc_delta( struct Senfe_res_table *new_res_table )
{	
	u32 i,j,tmp_delta,index;
	
	index = 0;
	for(i=0;new_res_table[i].name.IsEmpty()==0;i++)
	{
		tmp_delta = ~0;
		for(j=0;j < new_res_table[i].name.GetLength();j++)
		{
			index = new_res_table[i].name.GetAt(j) - 0x61;
			if(tmp_delta > all_res[index].deadline)
				tmp_delta = all_res[index].deadline;
		}
		new_res_table[i].deadline = tmp_delta;
	}
}

/* WARN: assuming that the WaitQueue is the wait-queue on time 't' */
/* WARN: assuming that the ReleaseQueue is the wait-queue on time 't' */
static double
feasibility_blocking(struct Senfe_task_abs **wait_queue, struct Senfe_task_abs **release_queue, u32 t)
{
	struct Senfe_task_abs *task;
	double bt = 0;
	
	for (task = *wait_queue;
	task != NULL;
	task = task->next)
	{
		// We only need to calculate the blocking load in the first task-cycle
		task_resource_use_ptr rp;

		if (t < task->attr->deadline)
		{	
			if ((rp = task->attr->resource_use) != NULL)
				for ( ; (rp->res_ptr) != NULL ; rp ++ )
					if (rp->res_ptr->deadline <= t && bt < rp->cost)
						bt = rp->cost;

		}
	}
	for (task = *release_queue;
	task != NULL;
	task = task->next)
	{
		// We only need to calculate the blocking load in the first task-cycle
		task_resource_use_ptr rp;
		if (t < task->attr->deadline)
		{
			if ((rp = task->attr->resource_use) != NULL)
				for ( ; (rp->res_ptr) != NULL ; rp ++ )
					if (rp->res_ptr->deadline <= t && bt < rp->cost)
						bt = rp->cost;
					

		}
	}
	return bt;
}

static int
feasibility_next(struct Senfe_task_abs **wait_queue,
				 struct Senfe_task_abs **release_queue,
				 u32 *t, u32 *c)
{
	
	struct Senfe_task_abs **q_nul;
	struct Senfe_task_abs **q_pos;
	int result;
	u32 n;
	if ((*release_queue != NULL)
		&& ((*wait_queue == NULL) || ((*wait_queue)->absolute >= (*release_queue)->absolute)))
	{
		// when both queues have equal absolute value,
		// the release queue has priority. (used for
		// checking whether H(t) + B(t) > t)
		result = 0;//deadline
		q_nul = release_queue;
		q_pos = wait_queue;
	}
	else
	{
		result = 1;//release 
		q_nul = wait_queue;
		q_pos = release_queue;
	}
	
	//assert( *q_nul != NULL );
	
	n = (*q_nul)->absolute;//past n tick
	(*q_nul)->absolute = 0;//as new zero point
	
	*t += n;
	
	if (*q_pos != NULL)
		(*q_pos)->absolute -= n;//decrease the past tick,keep synchronization
	
	do
	{
		struct Senfe_task_abs *task;
		
		task = task_abs_queue_first(q_nul);
		*c += task->attr->cputime;
		if (result)
			task->absolute = task->attr->deadline;//next deadline relative
		else
			task->absolute = task->attr->period - task->attr->deadline;//next release relative
		task_abs_queue_insert(q_pos, task);
	}
	while ((*q_nul != NULL) && ((*q_nul)->absolute == 0));//still have task time up
	
	return result;
}

int
feasibility_analysis(struct Senfe_task_abs *new_task_set)
{
	u32 t = 0;		// t_now
	
	u32 H = 0;		// work-load that needs to be resolved from t=0..now
	
	u32 W = 0;		// work-load offered from t=0..now
	float B = 0;		// blocking-load
	

					/* initialize task_set:
					* - 'task->absolute=0' -> release
					* - 'task->delta' with deadline inheritance
	*/
	struct Senfe_task_abs *wait_queue;
	struct Senfe_task_abs *release_queue;
	
	/* empty task set is always feasible */
	if (new_task_set == NULL)
		return 1;
	
	scheduler_calc_delta(new_task_set);
	
	wait_queue = new_task_set;
	release_queue = NULL;
	
	
	/* do analysis */
	while (1)
	{
		u32 C = 0; // cputime of the new feasibility-step
		
		switch (feasibility_next(&wait_queue, &release_queue, &t, &C))
		{
		case 0: // task wait deadline
			Wload_t[countw] = (float)t;
			Wload_W[countw++] = (float)W;
			Hload_t[counth] = (float)t;
			Hload_H[counth++] = (float)H;
			H += C;
			B = (float)feasibility_blocking(&wait_queue, &release_queue, t);
			Hload_t[counth] = (float)t;
			Hload_H[counth++] = (float)H;
			if(B>0){
 				Bload_t[countb] = (float)t;
 				Bload_B[countb++] = (float)H;
				Bload_t[countb] = (float)t;
				Bload_B[countb++] = (float)(H+B);
			}
			if((H + B) > t)
			{
				//feasibility_done(&wait_queue, &release_queue, new_task_set);
				timeend = t;
				return 0; // not schedulable
			}
			break;
			
		case 1: // task release
			Wload_t[countw] = (float)t;
			Wload_W[countw++] = (float)W;
			Hload_t[counth] = (float)t;
			Hload_H[counth++] = (float)H;
			if ((t > 0) && (W <= t))
			{
				//feasibility_done(&wait_queue, &release_queue, new_task_set);
				timeend = t;
				return 1; // schedulable
			}
			W += C;
			Wload_t[countw] = (float)t;
			Wload_W[countw++] = (float)W;
		
			break;
			
		default:
			break;
		}
	}
}



void CSenfeDlg::OnCancel() 
{
	// TODO: Add extra cleanup here
	
	CDialog::OnCancel();
	
}


/*clear all the input in the list control*/
void CSenfeDlg::OnClearButton() 
{
	// TODO: Add your control notification handler code here
	task_res_table_number = 0;				//the number of task_res_table
	
	task_number = 0;							//the number of task attr

	countw=0;
	counth=0;
	countb=0;

	m_tasklist.DeleteAllItems();
	m_feasibility.EnableWindow(FALSE);
	
}
